/**
 * @file CircBuff.c
 *
 * This file implements the circular buffer used in the streaming path.
 *
 * @author Luc Fourestier
 * @date 2009
 *
 * @page CB The circular buffer
 *
 * @section CB_Conv The naming conventions
 *
 * The circular buffer is a dedicated circular buffer for our streaming mechanism.
 *
 * @li We call "element", one cell of the circular buffer; The size of an element is set at creation (See 'MaxElementSize')
 * @li We call "buffer", the complete circular buffer with all its element. The number of element is set at creation (See 'MaxElementNumber')
 * @li We call "treatment", an action done on an element of the buffer (it can be read, write, modify...).
 *
 * @section CB_Overv The architecture
 *
 * It implements a write and a read, but also an analysis and a tagging treatment.
 * @li The write is the treatment which will fill the buffer.
 * @li The read is the treatment which will empty the buffer.
 * @li The analysis is the treatment that will analyze all the element. It does neither modify nor remove them.
 * @li The tagging is the treatment which will tag the element. It does not remove the element, only modification.
 *
 * It requires several indexes then (point of treament):
 * @li The write index, which points the element where to write the next entry.
 * @li The read index, which points the next element to be read.
 * @li The tag index, which points the next element to be tagged.
 * @li The analysis index, which points the next element to be analyzed.
 *
 * Second, it does implement buffer access in the way to avoid unnecessary element copies.
 * Thus, instead of copying, it directly plays with the element pointers, and the accesses are
 * directly done on the element itself (with all the danger it represents!!!).
 * Therefore, an element access in that buffer is always done in the following way:
 * @li 1- Get the pointer of the element to be treated, lock the circular buffer.
 * @li 2- if (element treated) => validate the treated element (increment the index), unlock the circular buffer.
 * @li 3- else (element not treated) => Release (or unlock) the circular buffer for the next treatment.
 *
 * @section CB_Design The design
 *
 * As said previously, the access to the buffer are done thanks to three functions:
 * @li CircB_GetXXXElt() (ex: CircB_GetWriteElt())
 * @li CircB_ValidateXXXElt() (ex: CircB_ValidateWriteElt())
 * @li CircB_ReleaseXXXElt() (ex: CircB_ReleaseWriteElt())
 *
 * Those three functions are repeated per treatment (Write, analysis...).
 *
 * In addition, a CircB_KillXXX() (ex: CircB_KillWrite()) is available to go out of any waiting state in the previous functions.
 *
 * TO BE COMPLETED
 *
 */

#include "CircBuff.h"
#include "Global.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAGIC 0x55


/**
 * Create a circular buffer and return its pointer.
 * CircBuff_t will be allocated here.
 *
 * @param CircBuff (out): The circular buffer info structure returned.
 * @param MaxElementSize (in): The size in byte of an element.
 * @param MaxElementNumber (in): The maximum number of element.
 * @return Errors
 */
int CircB_CreateCircBuff(CircBuff_t ** CircBuff_p, unsigned int MaxElementSize, unsigned int MaxElementNumber)
{
	CircBuff_t * CB_p;

	if (CircBuff_p == NULL)
	{
		return(CIRCB_ERROR_BAD_PARAM);
	}

	CB_p = (CircBuff_t*) malloc(sizeof(CircBuff_t));
	if (CB_p == NULL)
	{
		return(CIRCB_ERROR_CANNOT_ALLOCATE);
	}
	bzero((void*)CB_p, sizeof(CircBuff_t));

	if (pthread_mutex_init(&CB_p->CircBMutex, NULL) != 0)
	{
		return(ERROR);
	}

	if (pthread_mutex_init(&CB_p->ReadLock, NULL) != 0)
	{
		return(ERROR);
	}

	if (pthread_mutex_init(&CB_p->WriteLock, NULL) != 0)
	{
		return(ERROR);
	}

	if (pthread_mutex_init(&CB_p->TagLock, NULL) != 0)
	{
		return(ERROR);
	}

	if (pthread_mutex_init(&CB_p->AnalysisLock, NULL) != 0)
	{
		return(ERROR);
	}

	if (sem_init(&CB_p->ReadAccess, 0, 0) != 0)
	{
		return(ERROR);
	}

	if (sem_init(&CB_p->WriteAccess, 0, MaxElementNumber) != 0)
	{
		return(ERROR);
	}

	if (sem_init(&CB_p->TagAccess, 0, 0) != 0)
	{
		return(ERROR);
	}

	if (sem_init(&CB_p->AnalysisAccess, 0, 0) != 0)
	{
		return(ERROR);
	}

	CB_p->Buffer_p = (unsigned char*) malloc(MaxElementNumber * MaxElementSize);
	if (CB_p->Buffer_p == NULL)
	{
		return(CIRCB_ERROR_CANNOT_ALLOCATE);
	}
	bzero((void*)CB_p->Buffer_p, MaxElementNumber * MaxElementSize);

	CB_p->ElementSize = (unsigned int*) malloc(MaxElementNumber*sizeof(unsigned int));
	if (CB_p->ElementSize == NULL)
	{
		return(CIRCB_ERROR_CANNOT_ALLOCATE);
	}
	bzero((void*)CB_p->ElementSize, MaxElementNumber*sizeof(unsigned int));

	CB_p->MaxElementSize = MaxElementSize;
	CB_p->MaxElementNumber = MaxElementNumber;
	CB_p->Magic = MAGIC;

	*CircBuff_p = CB_p;
	return(OK);
}

/**
 * Delete the given circular buffer
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_DeleteCircBuff(CircBuff_t * CircBuff)
{
	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		pthread_mutex_lock(&CircBuff->CircBMutex);
		CircBuff->Magic = 0;

		if (CircBuff->Buffer_p != NULL)
		{
			free((void*)CircBuff->Buffer_p);
			CircBuff->Buffer_p = NULL;
		}

		if (CircBuff->ElementSize != NULL)
		{
			free((void*) CircBuff->ElementSize);
			CircBuff->ElementSize = NULL;
		}
		pthread_mutex_unlock(&CircBuff->CircBMutex);

		sem_destroy(&CircBuff->ReadAccess);
		sem_destroy(&CircBuff->WriteAccess);
		sem_destroy(&CircBuff->TagAccess);
		sem_destroy(&CircBuff->AnalysisAccess);

		pthread_mutex_destroy(&CircBuff->ReadLock);
		pthread_mutex_destroy(&CircBuff->WriteLock);
		pthread_mutex_destroy(&CircBuff->TagLock);
		pthread_mutex_destroy(&CircBuff->AnalysisLock);

		pthread_mutex_destroy(&CircBuff->CircBMutex);

		free(CircBuff);
	}
	else
	{
		return(ERROR);
	}

	return(OK);
}

/**
 * Get the pointer of the current write element and lock the write.
 *
 * @param CircBuff (in): The circular buffer.
 * @param Buff_p (out): The pointer to the element.
 * @param Size_p (out): The actual element size (can be entered as NULL).
 * @param Timeout (in): CIRCB_WAIT_FOREVER or CIRCB_NO_WAIT
 * @return Errors
 */
int CircB_GetWriteElt(CircBuff_t * CircBuff, void ** Elt_p, unsigned int * Size_p, int Timeout)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC) && (Elt_p != NULL))
	{
		pthread_mutex_lock(&CircBuff->WriteLock);

		// Wait (or not) for buffer not to be full
		if (Timeout == CIRCB_NO_WAIT)
		{
			RetVal = sem_trywait(&CircBuff->WriteAccess);
			if (RetVal != 0)
			{
				if(errno == EAGAIN)
				{
					RetVal = CIRCB_ERROR_BUFFER_FULL;
				}
				else
				{
					RetVal = ERROR;
				}
			}
		}
		else
		{
			CircBuff->KillWrite = 0;
			sem_wait(&CircBuff->WriteAccess);
			if (CircBuff->KillWrite == 1)
			{
				RetVal = CIRCB_ERROR_WAIT_KILLED;
			}
		}

		// Write
		if (RetVal == OK)
		{
			sem_post(&CircBuff->WriteAccess); // the semaphore must be really taken when the index will be incremented

			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->WriteIndex] == 0) // if element is empty
			{
				*Elt_p = CircBuff->Buffer_p + (CircBuff->WriteIndex * CircBuff->MaxElementSize);
			}
			else
			{
				RetVal = CIRCB_ERROR_BUFFER_FULL;
			}

			if (Size_p != NULL)
			{
				*Size_p = CircBuff->MaxElementSize;
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		// Check the result
		if (RetVal != OK) // Not an 'else' because RetVal can be modified in the previous if()
		{
			if (Size_p != NULL)
			{
				*Size_p = 0;
			}
			pthread_mutex_unlock(&CircBuff->WriteLock);
		}
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Validate the write on the current write element,
 * increment the write index and unlock the write.
 *
 * @param CircBuff (in): The circular buffer.
 * @param Size (in): The actual size written.
 * @return Errors
 */
int CircB_ValidateWriteElt(CircBuff_t * CircBuff, unsigned int Size)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC) && (Size > 0))
	{
		RetVal = sem_trywait(&CircBuff->WriteAccess);
		if (RetVal != 0)
		{
			if(errno == EAGAIN)
			{
				RetVal = CIRCB_ERROR_BUFFER_FULL;
			}
			else
			{
				RetVal = ERROR;
			}
		}

		if (RetVal == OK)
		{
			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->WriteIndex] == 0) // if element is empty
			{
				CircBuff->ElementSize[CircBuff->WriteIndex] = Size;
				CircBuff->WriteIndex++;
				CircBuff->WriteIndex %= CircBuff->MaxElementNumber;
				CircBuff->ElementCount++;
				sem_post(&CircBuff->AnalysisAccess); // Tell the analysis that something is ready
			}
			else
			{
				RetVal = CIRCB_ERROR_BUFFER_FULL;
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		pthread_mutex_unlock(&CircBuff->WriteLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Unlock the write.
 * Does not increment the WriteIndex.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ReleaseWriteElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		pthread_mutex_unlock(&CircBuff->WriteLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Kill the write waiting loop.
 * Only one loop!!
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_KillWrite(CircBuff_t * CircBuff)
{
	pthread_mutex_lock(&CircBuff->CircBMutex);
	CircBuff->KillWrite = 1;
	pthread_mutex_unlock(&CircBuff->CircBMutex);

	sem_post(&CircBuff->WriteAccess); // To flush one waiting semaphore

	return(OK);
}

/**
 * Get the pointer of the current read element and lock the read.
 *
 * @param CircBuff (in): The circular buffer.
 * @param Buff_p (out): The pointer to the element.
 * @param Size_p (out): The actual element size (can be entered as NULL).
 * @param Timeout (in): CIRCB_WAIT_FOREVER or CIRCB_NO_WAIT
 * @return Errors
 */
int CircB_GetReadElt(CircBuff_t * CircBuff, void ** Elt_p, unsigned int * Size_p, int Timeout)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC) && (Elt_p != NULL))
	{
		pthread_mutex_lock(&CircBuff->ReadLock);

		// Wait (or not) for buffer not to be full
		if (Timeout == CIRCB_NO_WAIT)
		{
			RetVal = sem_trywait(&CircBuff->ReadAccess);
			if (RetVal != 0)
			{
				if(errno == EAGAIN)
				{
					RetVal = CIRCB_ERROR_BUFFER_EMPTY;
				}
				else
				{
					RetVal = ERROR;
				}
			}
		}
		else
		{
			CircBuff->KillRead = 0;
			sem_wait(&CircBuff->ReadAccess);
			if (CircBuff->KillRead == 1)
			{
				RetVal = CIRCB_ERROR_WAIT_KILLED;
			}
		}

		// Read
		if (RetVal == OK)
		{
			sem_post(&CircBuff->ReadAccess); // the semaphore must be really taken when the index will be incremented

			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->ReadIndex] > 0) // if element is not empty
			{
				*Elt_p = CircBuff->Buffer_p + (CircBuff->ReadIndex * CircBuff->MaxElementSize);
			}
			else
			{
				RetVal = CIRCB_ERROR_BUFFER_EMPTY;
			}

			if (Size_p != NULL)
			{
				*Size_p = CircBuff->ElementSize[CircBuff->ReadIndex];
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		// Check the result
		if (RetVal != OK) // Not an 'else' because RetVal can be modified in the previous if()
		{
			if (Size_p != NULL)
			{
				*Size_p = 0;
			}
			pthread_mutex_unlock(&CircBuff->ReadLock);
		}
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Validate the read on the current read element,
 * increment the read index and unlock the read.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ValidateReadElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		RetVal = sem_trywait(&CircBuff->ReadAccess);
		if (RetVal != 0)
		{
			if(errno == EAGAIN)
			{
				RetVal = CIRCB_ERROR_BUFFER_EMPTY;
			}
			else
			{
				RetVal = ERROR;
			}
		}

		if (RetVal == OK)
		{
			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->ReadIndex] > 0) // if element is not empty
			{
				CircBuff->ElementSize[CircBuff->ReadIndex] = 0;
				CircBuff->ReadIndex++;
				CircBuff->ReadIndex %= CircBuff->MaxElementNumber;
				CircBuff->ElementCount--;
				sem_post(&CircBuff->WriteAccess); // Tell the write process that something is ready
			}
			else
			{
				RetVal = CIRCB_ERROR_BUFFER_EMPTY;
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		pthread_mutex_unlock(&CircBuff->ReadLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Unlock the read.
 * Does not increment the ReadIndex.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ReleaseReadElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		pthread_mutex_unlock(&CircBuff->ReadLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Kill the read waiting loop.
 * Only one loop!!
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_KillRead(CircBuff_t * CircBuff)
{
	pthread_mutex_lock(&CircBuff->CircBMutex);
	CircBuff->KillRead = 1;
	pthread_mutex_unlock(&CircBuff->CircBMutex);

	sem_post(&CircBuff->ReadAccess); // To flush one waiting semaphore

	return(OK);
}

/**
 * Get the pointer of the current tag element and lock the tagging.
 *
 * @param CircBuff (in): The circular buffer.
 * @param Buff_p (out): The pointer to the element.
 * @param Size_p (out): The actual element size (can be entered as NULL).
 * @param Timeout (in): CIRCB_WAIT_FOREVER or CIRCB_NO_WAIT
 * @return Errors
 */
int CircB_GetTagElt(CircBuff_t * CircBuff, void ** Elt_p, unsigned int * Size_p, int Timeout)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC) && (Elt_p != NULL))
	{
		pthread_mutex_lock(&CircBuff->TagLock);

		// Wait (or not) for tagging
		if (Timeout == CIRCB_NO_WAIT)
		{
			RetVal = sem_trywait(&CircBuff->TagAccess);
			if (RetVal != 0)
			{
				if(errno == EAGAIN)
				{
					RetVal = CIRCB_ERROR_TAG_EMPTY;
				}
				else
				{
					RetVal = ERROR;
				}
			}
		}
		else
		{
			CircBuff->KillTag = 0;
			sem_wait(&CircBuff->TagAccess);
			if (CircBuff->KillTag == 1)
			{
				RetVal = CIRCB_ERROR_WAIT_KILLED;
			}
		}

		// Tag
		if (RetVal == OK)
		{
			sem_post(&CircBuff->TagAccess); // the semaphore must be really taken when the index will be incremented

			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->TagIndex] > 0) // if element is not empty
			{
				*Elt_p = CircBuff->Buffer_p + (CircBuff->TagIndex * CircBuff->MaxElementSize);
			}
			else
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}

			if (Size_p != NULL)
			{
				*Size_p = CircBuff->ElementSize[CircBuff->TagIndex];
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		// Check the result
		if (RetVal != OK) // Not an 'else' because RetVal can be modified in the previous if()
		{
			if (Size_p != NULL)
			{
				*Size_p = 0;
			}
			pthread_mutex_unlock(&CircBuff->TagLock);
		}
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Validate the tag on the current tag element,
 * increment the tag index and unlock the tagging.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ValidateTagElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		RetVal = sem_trywait(&CircBuff->TagAccess);
		if (RetVal != 0)
		{
			if(errno == EAGAIN)
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}
			else
			{
				RetVal = ERROR;
			}
		}

		if (RetVal == OK)
		{
			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->TagIndex] > 0) // if element is not empty
			{
				CircBuff->TagIndex++;
				CircBuff->TagIndex %= CircBuff->MaxElementNumber;
				sem_post(&CircBuff->ReadAccess); // Tell the read process that something is ready
			}
			else
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		pthread_mutex_unlock(&CircBuff->TagLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Unlock the tag.
 * Does not increment the TagIndex.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ReleaseTagElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		pthread_mutex_unlock(&CircBuff->TagLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Kill the tag waiting loop.
 * Only one loop!!
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_KillTag(CircBuff_t * CircBuff)
{
	pthread_mutex_lock(&CircBuff->CircBMutex);
	CircBuff->KillTag = 1;
	pthread_mutex_unlock(&CircBuff->CircBMutex);

	sem_post(&CircBuff->TagAccess); // To flush one waiting semaphore

	return(OK);
}

/**
 * Get the pointer of the current analysis element and lock the analysis.
 *
 * @param CircBuff (in): The circular buffer.
 * @param Buff_p (out): The pointer to the element.
 * @param Size_p (out): The actual element size (can be entered as NULL).
 * @param Timeout (in): CIRCB_WAIT_FOREVER or CIRCB_NO_WAIT
 * @return Errors
 */
int CircB_GetAnalysisElt(CircBuff_t * CircBuff, void ** Elt_p, unsigned int * Size_p, int Timeout)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC) && (Elt_p != NULL))
	{
		pthread_mutex_lock(&CircBuff->AnalysisLock);

		// Wait (or not) for tagging
		if (Timeout == CIRCB_NO_WAIT)
		{
			RetVal = sem_trywait(&CircBuff->AnalysisAccess);
			if (RetVal != 0)
			{
				if(errno == EAGAIN)
				{
					RetVal = CIRCB_ERROR_TAG_EMPTY;
				}
				else
				{
					RetVal = ERROR;
				}
			}
		}
		else
		{
			CircBuff->KillAnalysis = 0;
			sem_wait(&CircBuff->AnalysisAccess);
			if (CircBuff->KillAnalysis == 1)
			{
				RetVal = CIRCB_ERROR_WAIT_KILLED;
			}
		}

		// Analysis
		if (RetVal == OK)
		{
			sem_post(&CircBuff->AnalysisAccess); // the semaphore must be really taken when the index will be incremented

			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->AnalysisIndex] > 0) // if element is not empty
			{
				*Elt_p = CircBuff->Buffer_p + (CircBuff->AnalysisIndex * CircBuff->MaxElementSize);
			}
			else
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}

			if (Size_p != NULL)
			{
				*Size_p = CircBuff->ElementSize[CircBuff->AnalysisIndex];
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		// Check the result
		if (RetVal != OK) // Not an 'else' because RetVal can be modified in the previous if()
		{
			if (Size_p != NULL)
			{
				*Size_p = 0;
			}
			pthread_mutex_unlock(&CircBuff->AnalysisLock);
		}
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Validate the analysis on the current analysis element,
 * increment the analysis index and unlock the analysis.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ValidateAnalysisElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		RetVal = sem_trywait(&CircBuff->AnalysisAccess);
		if (RetVal != 0)
		{
			if(errno == EAGAIN)
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}
			else
			{
				RetVal = ERROR;
			}
		}

		if (RetVal == OK)
		{
			pthread_mutex_lock(&CircBuff->CircBMutex);
			if (CircBuff->ElementSize[CircBuff->AnalysisIndex] > 0) // if element is not empty
			{
				CircBuff->AnalysisIndex++;
				CircBuff->AnalysisIndex %= CircBuff->MaxElementNumber;
				sem_post(&CircBuff->TagAccess); // Tell the tagging that something is ready
			}
			else
			{
				RetVal = CIRCB_ERROR_TAG_EMPTY;
			}
			pthread_mutex_unlock(&CircBuff->CircBMutex);
		}

		pthread_mutex_unlock(&CircBuff->AnalysisLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Unlock the analysis.
 * Does not increment the AnalysisIndex.
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_ReleaseAnalysisElt(CircBuff_t * CircBuff)
{
	int RetVal = OK;

	if ((CircBuff != NULL) && (CircBuff->Magic == MAGIC))
	{
		pthread_mutex_unlock(&CircBuff->AnalysisLock);
	}
	else
	{
		RetVal = CIRCB_ERROR_BAD_PARAM;
	}

	return(RetVal);
}

/**
 * Kill the analysis waiting loop.
 * Only one loop!!
 *
 * @param CircBuff (in): The circular buffer.
 * @return Errors
 */
int CircB_KillAnalysis(CircBuff_t * CircBuff)
{
	pthread_mutex_lock(&CircBuff->CircBMutex);
	CircBuff->KillAnalysis = 1;
	pthread_mutex_unlock(&CircBuff->CircBMutex);

	sem_post(&CircBuff->AnalysisAccess); // To flush one waiting semaphore

	return(OK);
}

